using System;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;

public class TcpContent : MonoBehaviour
{
    private const int headLength = 4;


    [SerializeField] private string ip = "192.168.5.241";
    // [SerializeField] private string ip = "2408:820c:d0d:3d00::cbd";

    [SerializeField] private int port = 65532;

    private readonly byte[] _headBytes = new byte[headLength];

    private readonly ConcurrentQueue<byte[]> _needHandleBytes = new();

    private NetworkStream _stream;
    private TcpClient _tcpClient;

    public bool Connected => _tcpClient.Connected;

    private void Start()
    {
        Connect();
    }

    private void Update()
    {
        if (!_needHandleBytes.IsEmpty)
        {
            _needHandleBytes.TryDequeue(out var dataBytes);
            HandleStr(dataBytes);
        }

        if (!Connected) Debug.Log("Tcp已断开");

        #region 测试代码

        if (Input.GetKeyDown(KeyCode.Space))
            SendString(
                "-- for new json file export from ccs2.x \n\n\nlocal UILoaderUtilitys = import(\".UILoaderUtilitys\")\nlocal CCSUILoader = class(\"CCSUILoader\")\n\nfunction CCSUILoader:load(json, params)\n\tif params then\n\t\tself.bUseEditBox = params.bUseEditBox or false\n\telse\n\t\tself.bUseEditBox = false\n\tend\n\n\tif cc.bPlugin_ and self.bUseEditBox then\n\t\t-- CCEditbox(C++ widget) not support no background\n\t\tself.bUseEditBox = false\n\n\t\tprint(\"Error! not support CCEditbox in cocostudio layout file\")\n\tend\n\n\tself.texturesPng = json.texturesPng\n\tself:loadTexture(json)\n\n\tlocal node, bAdaptScreen = self:parserJson(json)\n\tself.texturesPng = nil\n\tif bAdaptScreen then\n\t\treturn node, display.width, display.height\n\telse\n\t\treturn node, json.designWidth, json.designHeight\n\tend\nend\n\nfunction CCSUILoader:loadFile(jsonFile, params)\n\tlocal fileUtil = cc.FileUtils:getInstance()\n\tlocal fullPath = fileUtil:fullPathForFilename(jsonFile)\n\tlocal jsonStr = fileUtil:getStringFromFile(fullPath)\n\tlocal jsonVal = json.decode(jsonStr)\n\n\tUILoaderUtilitys.addSearchPathIf(io.pathinfo(fullPath).dirname)\n\n\tlocal node, w, h = self:load(jsonVal, params)\n\n\tUILoaderUtilitys.clearPath()\n\n\treturn node, w, h\nend\n\nfunction CCSUILoader:parserJson(jsonVal)\n\tlocal root = jsonVal.Content.Content.ObjectData\n\tif not root then\n\t\tprintInfo(\"CCSUILoader - parserJson havn't found root node\")\n\t\treturn\n\tend\n\tself:prettyJson(root)\n\tlocal uiRoot = self:generateUINode(root)\n\n\treturn uiRoot\nend\n\n-- generate a ui node and invoke self to generate child ui node\nfunction CCSUILoader:generateUINode(jsonNode, parent)\n\tlocal clsName = string.sub(jsonNode.ctype, 1, -11)\n\tlocal uiNode = self:createUINode(clsName, jsonNode, parent)\n\tif not uiNode then\n\t\treturn\n\tend\n\n\tuiNode.name = jsonNode.Name or \"unknow node\"\n\n\t--for seek\n\tuiNode.subChildren = {}\n\tif parent then\n\t\tparent.subChildren[uiNode.name] = uiNode\n\tend\n\n\t-- print(\"CCSUILoader set node params:\" .. uiNode.name)\n\n\tif uiNode.setFlippedX then\n\t\tuiNode:setFlippedX(jsonNode.FlipX or false)\n\tend\n\tif uiNode.setFlippedY then\n\t\tuiNode:setFlippedY(jsonNode.FlipY or false)\n\tend\n\n\tuiNode:setTag(jsonNode.Tag or 0)\n\tuiNode:setRotation(jsonNode.Rotation or 0)\n\tuiNode:setSkewX(jsonNode.RotationSkewX or 0)\n\tuiNode:setSkewY(jsonNode.RotationSkewY or 0)\n\n\t-- uiNode:setVisible(jsonNode.visible) -- ccs havn't export visible attribute\n\tif jsonNode.Scale then\n\t\tuiNode:setScaleX((jsonNode.Scale.ScaleX or 1) * uiNode:getScaleX())\n\t\tuiNode:setScaleY((jsonNode.Scale.ScaleY or 1) * uiNode:getScaleY())\n\tend\n\n\n\n\tlocal emptyNode\n\tif \"ScrollView\" == clsName then\n\t\temptyNode = cc.Node:create()\n\t\temptyNode:setPosition(jsonNode.Position.X, jsonNode.Position.Y)\n\t\tuiNode:addScrollNode(emptyNode)\n\tend\n\n\tlocal children = jsonNode.Children\n\tif children then\n\tfor i,v in ipairs(children) do\n\t\tlocal childrenNode = self:generateUINode(v, uiNode)\n\t\tif childrenNode then\n\t\t\tif \"ScrollView\" == clsName then\n\t\t\t\temptyNode:addChild(childrenNode)\n\t\t\telseif \"ListView\" == clsName then\n\t\t\t\tlocal item = uiNode:newItem()\n\t\t\t\titem:addContent(childrenNode)\n\t\t\t\tlocal size = childrenNode:getContentSize()\n\t\t\t\titem:setItemSize(size.width, size.height)\n\t\t\t\tuiNode:addItem(item)\n\n\t\t\t\tif \"Button\" == v.classname then\n\t\t\t\t\tchildren:setTouchSwallowEnabled(false)\n\t\t\t\tend\n\t\t\telseif \"PageView\" == clsName then\n\t\t\t\tlocal item = uiNode:newItem()\n\t\t\t\tchildrenNode:setPosition(0, 0)\n\t\t\t\titem:addChild(childrenNode)\n\t\t\t\titem:setTag(10001)\n\t\t\t\tuiNode:addItem(item)\n\t\t\telse\n\t\t\t\tuiNode:addChild(childrenNode)\n\t\t\tend\n\t\tend\n\tend\n\tend\n\n\tif \"ListView\" == clsName or \"PageView\" == clsName then\n\t\tuiNode:reload()\n\telseif \"ScrollView\" == clsName then\n\t\tuiNode:resetPosition()\n\tend\n\n\treturn uiNode\nend\n\nfunction CCSUILoader:createUINode(clsName, options, parent)\n\tif not clsName then\n\t\treturn\n\tend\n\n\tprintInfo(\"CCSUILoader - createUINode:\" .. clsName)\n\n\tlocal node\n\n\tif clsName == \"SingleNode\" or clsName == \"Node\" then\n\t\tnode = self:createNode(options)\n\telseif clsName == \"Sprite\" or clsName == \"Scale9Sprite\" then\n\t\tnode = self:createSprite(options)\n\telseif clsName == \"ImageView\" then\n\t\tnode = self:createImage(options)\n\telseif clsName == \"Button\" then\n\t\tnode = self:createButton(options)\n\telseif clsName == \"LoadingBar\" then\n\t\tnode = self:createLoadingBar(options)\n\telseif clsName == \"Slider\" then\n\t\tnode = self:createSlider(options)\n\telseif clsName == \"CheckBox\" then\n\t\tnode = self:createCheckBox(options)\n    elseif clsName == \"TextBMFont\" then\n    \tnode = self:createBMFontLabel(options)\n    elseif clsName == \"Text\" then\n    \tnode = self:createLabel(options)\n    elseif clsName == \"LabelAtlas\" then\n    \tnode = self:createLabelAtlas(options)\n\telseif clsName == \"TextField\" then\n\t\tnode = self:createEditBox(options)\n\telseif clsName == \"Panel\" then\n\t\tnode = self:createPanel(options)\n\telseif clsName == \"ScrollView\" then\n\t\tnode = self:createScrollView(options)\n\telseif clsName == \"ListView\" then\n\t\tnode = self:createListView(options)\n\telseif clsName == \"PageView\" then\n\t\tnode = self:createPageView(options)\n\telseif clsName == \"ProjectNode\" then\n\t\tnode = self:createProjectNode(options)\n\telseif clsName == \"Layer\" then\n        node = self:createLayer(options)\n\telse\n\t\tprintInfo(\"CCSUILoader not support node:\" .. clsName)\n\tend\n\n\treturn node\nend\n\nfunction CCSUILoader:getChildOptionJson(json)\n\treturn json.options.layoutParameter\nend\n\nfunction CCSUILoader:newWapperNode(oldNode, layoutParameter)\n\tlocal newNode = display.newNode()\n\tlocal size = oldNode:getContentSize()\n\tsize.width = size.width + layoutParameter.marginLeft + layoutParameter.marginRight\n\tsize.height = size.height + layoutParameter.marginTop + layoutParameter.marginDown\n\tnewNode:setContentSize(size)\n\tnewNode:addChild(oldNode)\n\toldNode:setPosition()\nend\n\nfunction CCSUILoader:getButtonStateImages(options)\n\tlocal images = {}\n\tif options.NormalFileData then\n\t\timages.normal = self:transResName(options.NormalFileData)\n\tend\n\tif options.PressedFileData then\n\t\timages.pressed = self:transResName(options.PressedFileData)\n\tend\n\tif options.DisabledFileData then\n\t\timages.disabled = self:transResName(options.DisabledFileData)\n\tend\n\n\treturn images\nend\n\nfunction CCSUILoader:getAnchorType(anchorX, anchorY)\n\tif 1 == anchorX then\n\t\tif 1 == anchorY then\n\t\t\treturn display.RIGHT_TOP\n\t\telseif 0.5 == anchorY then\n\t\t\treturn display.RIGHT_CENTER\n\t\telse\n\t\t\treturn display.RIGHT_BOTTOM\n\t\tend\n\telseif 0.5 == anchorX then\n\t\tif 1 == anchorY then\n\t\t\treturn display.CENTER_TOP\n\t\telseif 0.5 == anchorY then\n\t\t\treturn display.CENTER\n\t\telse\n\t\t\treturn display.CENTER_BOTTOM\n\t\tend\n\telse\n\t\tif 1 == anchorY then\n\t\t\treturn display.LEFT_TOP\n\t\telseif 0.5 == anchorY then\n\t\t\treturn display.LEFT_CENTER\n\t\telse\n\t\t\treturn display.LEFT_BOTTOM\n\t\tend\n\tend\nend\n\n\nfunction CCSUILoader:getCheckBoxImages(options)\n\tlocal images = {}\n\n\tlocal getBackgroundImage = function(state)\n\t\tlocal image = options.NormalBackFileData\n\t\tif \"pressed\" == state then\n\t\t\timage = options.PressedBackFileData\n\t\telseif \"disabled\" == state then\n\t\t\timage = options.DisableBackFileData\n\t\tend\n\n\t\treturn image\n\tend\n\n\t-- local getFrontgroundImage = function(state)\n\t-- \tlocal image = options.backGroundBoxData.path\n\t-- \tif \"pressed\" == state then\n\t-- \t\timage = options.backGroundBoxSelectedData.path\n\t-- \tend\n\t-- \tif \"disabled\" == state then\n\t-- \t\timage = options.backGroundBoxDisabledData.path\n\t-- \tend\n\n\t-- \treturn image\n\t-- end\n\n\timages.off = self:transResName(getBackgroundImage(\"normal\"))\n\timages.off_pressed = self:transResName(getBackgroundImage(\"pressed\"))\n\timages.off_disabled = self:transResName(getBackgroundImage(\"disabled\"))\n\timages.on = {images.off, self:transResName(options.NodeNormalFileData)}\n\timages.on_pressed = images.on\n\timages.on_disabled = {images.off_disabled,\n\t\tself:transResName(options.NodeDisableFileData)}\n\n\treturn images\nend\n\nfunction CCSUILoader:loadTexture(json)\n\t-- cc.FileUtils:getInstance():addSearchPath(\"res/\")\n\n\tif not json.textures then\n\t\treturn\n\tend\n\n\tfor i,v in ipairs(json.textures) do\n\t\tself.bUseTexture = true\n\t\t-- if json.texturesPng then\n\t\t-- \tpng = self:getTexturePng(json.texturesPng[i])\n\t\t-- end\n\t\tUILoaderUtilitys.loadTexture(v)\n\tend\n\nend\n\nfunction CCSUILoader:getTexturePng(plist)\n\tif not plist then\n\t\treturn\n\tend\n\n\tlocal info = io.pathinfo(plist)\n\n\tlocal png\n\tif info.dirname then\n\t\tpng = info.dirname .. info.basename .. \".png\"\n\telse\n\t\tpng = info.basename .. \".png\"\n\tend\n\n\treturn png\nend\n\nfunction CCSUILoader:transResName(fileData)\n\tif not fileData then\n\t\treturn\n\tend\n\n\tlocal name = fileData.Path\n\tif not name then\n\t\treturn name\n\tend\n\n\tUILoaderUtilitys.loadTexture(fileData.Plist)\n\tif \"PlistSubImage\" == fileData.Type then\n\t\treturn \"#\" .. name\n\telse\n\t\treturn UILoaderUtilitys.getFileFullName(name)\n\tend\nend\n\nfunction CCSUILoader:createNode(options)\n\tlocal node = cc.Node:create()\n\tif options.Size then\n\t\tnode:setContentSize(cc.size(options.Size.X or 0, options.Size.Y or 0))\n\tend\n\tnode:setPositionX(options.Position.X or 0)\n\tnode:setPositionY(options.Position.Y or 0)\n\tnode:setAnchorPoint(\n\t\tcc.p(options.AnchorPoint.ScaleX or 0, options.AnchorPoint.ScaleY or 0))\n\n\treturn node\nend\n\nfunction CCSUILoader:createLayer(options)\n    local node = cc.Layer:create()\n    if options.Size then\n        node:setContentSize(cc.size(options.Size.X or 0, options.Size.Y or 0))\n    end\n    node:setPositionX(options.Position.X or 0)\n    node:setPositionY(options.Position.Y or 0)\n    node:setAnchorPoint(\n        cc.p(options.AnchorPoint.ScaleX or 0, options.AnchorPoint.ScaleY or 0))\n\n    return node\nend\n\nfunction CCSUILoader:createSprite(options)\n\tlocal node = display.newSprite(self:transResName(options.FileData))\n\tif options.Size then\n\t\tnode:setContentSize(cc.size(options.Size.X or 0, options.Size.Y or 0))\n\tend\n\tif options.Alpha then\n\t\tnode:setOpacity(options.Alpha)\n\tend\n\tnode:setPositionX(options.Position.X or 0)\n\tnode:setPositionY(options.Position.Y or 0)\n\tnode:setAnchorPoint(\n\t\tcc.p(options.AnchorPoint.ScaleX or 0, options.AnchorPoint.ScaleY or 0))\n\n\treturn node\nend\n\nfunction CCSUILoader:createImage(options)\n\tlocal params = {}\n\tparams.scale9 = options.Scale9Enable\n\tif params.scale9 then\n\t\tparams.capInsets = cc.rect(options.Scale9OriginX, options.Scale9OriginY,\n\t\t\toptions.Scale9Width, options.Scale9Height)\n\tend\n\tlocal node = cc.ui.UIImage.new(self:transResName(options.FileData), params)\n\n\tif not options.Scale9Enable then\n\t\tlocal originSize = node:getContentSize()\n\t\tif options.Size.X then\n\t\t\toptions.Scale.ScaleX = (options.Scale.ScaleX or 1) * options.Size.X/originSize.width\n\t\tend\n\t\tif options.Size.Y then\n\t\t\toptions.Scale.ScaleY = (options.Scale.ScaleY or 1) * options.Size.Y/originSize.height\n\t\tend\n\tend\n\tif options.Size then\n\t\tnode:setLayoutSize(options.Size.X, options.Size.Y)\n\tend\n\tnode:setPositionX(options.Position.X or 0)\n\tnode:setPositionY(options.Position.Y or 0)\n\tnode:setAnchorPoint(\n\t\tcc.p(options.AnchorPoint.ScaleX or 0, options.AnchorPoint.ScaleY or 0))\n\n\tif options.TouchEnable then\n\t\tnode:setTouchEnabled(true)\n\t\tnode:setTouchSwallowEnabled(true)\n\tend\n\tif options.Alpha then\n\t\tnode:setOpacity(options.Alpha)\n\tend\n\n\treturn node\nend\n\nfunction CCSUILoader:createButton(options)\n\tlocal node = cc.ui.UIPushButton.new(self:getButtonStateImages(options),\n\t\t{scale9 = options.Scale9Enable,\n\t\tflipX = options.FlipX,\n\t\tflipY = options.FlipY})\n\n\tif options.Alpha then\n\t\tnode:setCascadeOpacityEnabled(true)\n\t\tnode:setOpacity(options.Alpha)\n\tend\n\tif options.ButtonText then\n\t\tnode:setButtonLabel(\n\t\t\tcc.ui.UILabel.new({text = options.ButtonText,\n\t\t\t\tsize = options.FontSize,\n\t\t\t\tfont = options.FontResource and options.FontResource.Path,\n\t\t\t\tcolor = cc.c3b(options.TextColor.R, options.TextColor.G, options.TextColor.B)}))\n\tend\n\tif options.Size then\n\t\tnode:setButtonSize(options.Size.X, options.Size.Y)\n\tend\n\tnode:align(self:getAnchorType(options.AnchorPoint.ScaleX or 0, options.AnchorPoint.ScaleY or 0),\n\t\toptions.Position.X or 0, options.Position.Y or 0)\n\n\treturn node\nend\n\nfunction CCSUILoader:createLoadingBar(options)\n\tlocal params = {}\n\tparams.image = self:transResName(options.ImageFileData)\n\tparams.scale9 = options.Scale9Enable\n\tparams.capInsets = cc.rect(options.Scale9OriginX, options.Scale9OriginY,\n\t\t\toptions.Scale9Width, options.Scale9Height)\n\tif \"Right_To_Left\" == options.ProgressType then\n\t\tparams.direction = cc.ui.UILoadingBar.DIRECTION_RIGHT_TO_LEFT\n\telse\n\t\tparams.direction = cc.ui.UILoadingBar.DIRECTION_LEFT_TO_RIGHT\n\tend\n\tparams.percent = options.ProgressInfo or 100\n\tparams.viewRect = cc.rect(options.Position.X, options.Position.Y, options.Size.X, options.Size.Y)\n\n\tlocal node = cc.ui.UILoadingBar.new(params)\n\n\tnode:setDirction(params.direction)\n\tnode:setPositionX(options.Position.X or 0)\n\tnode:setPositionY(options.Position.Y or 0)\n\tnode:setContentSize(options.Size.X, options.Size.Y)\n\tnode:setAnchorPoint(\n\t\tcc.p(options.AnchorPoint.ScaleX or 0, options.AnchorPoint.ScaleY or 0))\n\n\treturn node\nend\n\nfunction CCSUILoader:createSlider(options)\n\tlocal node = cc.ui.UISlider.new(display.LEFT_TO_RIGHT,\n\t\t{bar = self:transResName(options.BackGroundData),\n\t\tbarfg = self:transResName(options.ProgressBarData),\n\t\tbutton = self:transResName(options.BallNormalData),\n\t\tbutton_pressed = self:transResName(options.BallPressedData),\n\t\tbutton_disabled = self:transResName(options.BallDisabledData),},\n\t\t{scale9 = options.Scale9Enable})\n\n\tif options.Size then\n\t\tnode:setSliderSize(options.Size.X, options.Size.Y)\n\tend\n\tnode:align(self:getAnchorType(options.AnchorPoint.ScaleX or 0, options.AnchorPoint.ScaleY or 0),\n\t\toptions.Position.X or 0, options.Position.Y or 0)\n    node:setSliderValue(options.PercentInfo)\n\n\treturn node\nend\n\nfunction CCSUILoader:createCheckBox(options)\n\tlocal node = cc.ui.UICheckBoxButton.new(\n\t\tself:getCheckBoxImages(options))\n\n\tnode:align(self:getAnchorType(options.AnchorPoint.ScaleX or 0, options.AnchorPoint.ScaleY or 0),\n\t\toptions.Position.X or 0, options.Position.Y or 0)\n\n\treturn node\nend\n\nfunction CCSUILoader:createBMFontLabel(options)\n\tlocal node = cc.ui.UILabel.new({\n\t\tUILabelType = 1,\n\t\ttext = options.LabelText,\n\t\tfont = options.LabelBMFontFile_CNB.Path,\n\t\ttextAlign = cc.ui.TEXT_ALIGN_CENTER})\n\tnode:setColor(cc.c4b(options.CColor.R or 255, options.CColor.G or 255, options.CColor.B or 255, options.Alpha or 255))\n\tnode:align(self:getAnchorType(options.AnchorPoint.ScaleX or 0, options.AnchorPoint.ScaleY or 0),\n\t\toptions.Position.X or 0, options.Position.Y or 0)\n\n\treturn node\nend\n\nfunction CCSUILoader:createLabel(options)\n\tlocal hAlignment\n\tif \"HT_Center\" == options.HorizontalAlignmentType then\n\t\thAlignment = cc.TEXT_ALIGNMENT_CENTER\n\telseif \"HT_Right\" == options.HorizontalAlignmentType then\n\t\thAlignment = cc.TEXT_ALIGNMENT_RIGHT\n\telse\n\t\thAlignment = cc.TEXT_ALIGNMENT_LEFT\n\tend\n\n\tlocal vAlignment\n\tif \"VT_Center\" == options.VerticalAlignmentType then\n\t\tvAlignment = cc.VERTICAL_TEXT_ALIGNMENT_CENTER\n\telseif \"VT_Bottom\" == options.VerticalAlignmentType then\n\t\tvAlignment = cc.VERTICAL_TEXT_ALIGNMENT_BOTTOM\n\telse\n\t\tvAlignment = cc.VERTICAL_TEXT_ALIGNMENT_TOP\n\tend\n\n\tlocal node = cc.ui.UILabel.new({text = options.LabelText,\n\t\tfont = options.FontResource and options.FontResource.Path,\n\t\tsize = options.FontSize,\n\t\tcolor = cc.c3b(options.CColor.R or 255, options.CColor.G or 255, options.CColor.B or 255),\n\t\talign = hAlignment,\n\t\tvalign = vAlignment,\n\t\tdimensions = options.IsCustomSize and cc.size(options.Size.X or 0, options.Size.Y or 0),\n\t\tx = options.Position.X, y = options.Position.Y})\n\n\tnode:align(self:getAnchorType(options.AnchorPoint.ScaleX or 0, options.AnchorPoint.ScaleY or 0),\n\t\toptions.Position.X or 0, options.Position.Y or 0)\n\n\treturn node\nend\n\nfunction CCSUILoader:createLabelAtlas(options)\n\tlocal labelAtlas\n\tif \"function\" == type(cc.LabelAtlas._create) then\n\t\tlabelAtlas = cc.LabelAtlas:_create()\n\t\tlabelAtlas:initWithString(options.stringValue,\n\t\t\toptions.charMapFileData.path,\n\t\t\toptions.itemWidth,\n\t\t\toptions.itemHeight,\n\t\t\tstring.byte(options.startCharMap))\n\telse\n\t\tlabelAtlas = cc.LabelAtlas:create(\n\t\t\toptions.stringValue,\n\t\t\toptions.charMapFileData.path,\n\t\t\toptions.itemWidth,\n\t\t\toptions.itemHeight,\n\t\t\tstring.byte(options.startCharMap))\n\tend\n\n\tlabelAtlas:setAnchorPoint(\n\t\tcc.p(options.anchorPointX or 0.5, options.anchorPointY or 0.5))\n\tlabelAtlas:setPosition(options.x, options.y)\n\tif not options.ignoreSize then\n\t\tlabelAtlas:setContentSize(options.width, options.height)\n\tend\n\treturn labelAtlas\nend\n\nfunction CCSUILoader:createEditBox(options)\n\tlocal editBox\n\n\tif self.bUseEditBox then\n\t\teditBox = cc.ui.UIInput.new({\n\t\t\tUIInputType = 1,\n\t        size = cc.size(options.width, options.height)\n\t    \t})\n\t    editBox:setPlaceHolder(options.placeHolder)\n\t    editBox:setFontName(options.fontName)\n\t    editBox:setFontSize(options.fontSize or 20)\n\t    editBox:setText(options.text)\n\t    if options.passwordEnable then\n\t    \teditBox:setInputFlag(cc.EDITBOX_INPUT_FLAG_PASSWORD)\n\t\tend\n\t\tif options.maxLengthEnable then\n\t\t\teditBox:setMaxLength(options.maxLength)\n\t\tend\n\t\teditBox:setPosition(options.x, options.y)\n\telse\n\t\teditBox = cc.ui.UIInput.new({\n\t\tUIInputType = 2,\n        placeHolder = options.PlaceHolderText,\n        x = options.Position.X,\n        y = options.Position.Y,\n        text = options.LabelText,\n        size = cc.size(options.Size.X, options.Size.Y),\n        passwordEnable = options.PasswordEnable,\n        font = options.FontResource and options.FontResource.Path,\n        fontSize = options.FontSize,\n        maxLength = options.MaxLengthEnable and options.MaxLengthText\n        })\n\tend\n\n\teditBox:setAnchorPoint(\n\t\tcc.p(options.AnchorPoint.ScaleX or 0, options.AnchorPoint.ScaleY or 0))\n\n\treturn editBox\nend\n\nfunction CCSUILoader:createPanel(options)\n\tlocal node\n\tif options.ClipAble then\n\t\tnode = cc.ClippingRegionNode:create()\n\telse\n\t\tnode = display.newNode()\n\tend\n\tlocal clrLayer\n\tlocal bgLayer\n\n\tif 1 == options.ComboBoxIndex then\n\t\t-- single color\n\t\tclrLayer = cc.LayerColor:create()\n\t\tif not cc.bPlugin_ then\n\t\t\tclrLayer:resetCascadeBoundingBox()\n\t\tend\n\t\tclrLayer:setTouchEnabled(false)\n\t\tclrLayer:setColor(cc.c3b(options.SingleColor.R or 255,\n\t\t\toptions.SingleColor.G or 255, options.SingleColor.B or 255))\n\telseif 2 == options.ComboBoxIndex then\n\t\t-- gradient\n\t\tclrLayer = cc.LayerGradient:create()\n\t\tif not cc.bPlugin_ then\n\t\t\tclrLayer:resetCascadeBoundingBox()\n\t\tend\n\t\tclrLayer:setTouchEnabled(false)\n\t\tclrLayer:setStartColor(cc.c3b(options.FirstColor.R or 255,\n\t\t\toptions.FirstColor.G or 255, options.FirstColor.B or 255))\n\t\tclrLayer:setEndColor(cc.c3b(options.EndColor.R or 255,\n\t\t\toptions.EndColor.G or 255, options.EndColor.B or 255))\n\t\tclrLayer:setVector(cc.p(options.ColorVector.ScaleX or 0, options.ColorVector.ScaleY or 0))\n\tend\n\n\tif clrLayer then\n\t\tclrLayer:setAnchorPoint(cc.p(0, 0))\n\t\tclrLayer:setOpacity(options.BackColorAlpha or 255)\n\tend\n\n\t-- background layer\n\tif options.Scale9Enable then\n\t\tif options.FileData and options.FileData.Path then\n\t\t\tlocal capInsets = cc.rect(options.Scale9OriginX, options.Scale9OriginY,\n\t\t\t\t\t\toptions.Scale9Width, options.Scale9Height)\n\t\t\tlocal scale9sp = ccui.Scale9Sprite or cc.Scale9Sprite\n\t\t\tif \"Normal\" == options.FileData.Type then\n\t\t\t\tbgLayer = scale9sp:create(\n\t\t\t\t\tcapInsets, options.FileData.Path)\n\t\t\telse\n\t\t\t\tbgLayer = scale9sp:createWithSpriteFrameName(\n\t\t\t\t\toptions.FileData.Path, capInsets)\n\t\t\tend\n\t\t\tbgLayer:setContentSize(cc.size(options.Size.X, options.Size.Y))\n\t\tend\n\telse\n\t\tif options.FileData and options.FileData.Path then\n\t\t\tbgLayer = display.newSprite(\n\t\t\t\tself:transResName(options.FileData))\n\t\tend\n\tend\n\n\tlocal conSize\n\tconSize = cc.size(options.Size.X, options.Size.Y)\n\n\tif options.ClipAble then\n\t\tnode:setClippingRegion(cc.rect(0, 0, options.Size.X, options.Size.Y))\n\tend\n\tif clrLayer then\n\t\tclrLayer:setContentSize(conSize)\n\tend\n\tif bgLayer then\n\t\tbgLayer:setPosition(conSize.width/2, conSize.height/2)\n\tend\n\tnode:setContentSize(conSize)\n\tif clrLayer then\n\t\tnode:addChild(clrLayer)\n\tend\n\tif bgLayer then\n\t\tnode:addChild(bgLayer)\n\tend\n\tnode:setPositionX(options.Position.X or 0)\n\tnode:setPositionY(options.Position.Y or 0)\n\tnode:setAnchorPoint(\n\t\tcc.p(options.AnchorPoint.ScaleX or 0, options.AnchorPoint.ScaleY or 0))\n\n\treturn node\nend\n\nfunction CCSUILoader:createScrollView(options)\n\tlocal params =\n\t\t{viewRect = cc.rect(options.Position.X, options.Position.Y, options.Size.X, options.Size.Y)}\n\n\tif 1 == options.ComboBoxIndex then\n\t\t-- single color\n\t\tparams.bgColor = cc.c4b(options.SingleColor.R or 255, options.SingleColor.G or 255, options.SingleColor.B or 255, options.BackColorAlpha or 255)\n\telseif 2 == options.ComboBoxIndex then\n\t\t-- gradient\n\t\tparams.bgStartColor = cc.c4b(options.FirstColor.R or 255, options.FirstColor.G or 255, options.FirstColor.B or 255, options.BackColorAlpha or 255)\n\t\tparams.bgEndColor = cc.c4b(options.EndColor.R or 255, options.EndColor.G or 255, options.EndColor.B or 255, options.BackColorAlpha or 255)\n\t\tparams.bgVector = cc.p(options.ColorVector.ScaleX, options.ColorVector.ScaleY)\n\tend\n\tparams.bg = self:transResName(options.FileData)\n\tif options.Scale9Enable then\n\t\tparams.bgScale9 = options.Scale9Enable\n\t\tparams.capInsets = cc.rect(options.Scale9OriginX, options.Scale9OriginY,\n\t\t\t\t\t\toptions.Scale9Width, options.Scale9Height)\n\tend\n\n\tlocal node = cc.ui.UIScrollView.new(params)\n\tlocal dir = 0\n\t-- quick 0:both 1:vertical 2:horizontal\n\tif \"Vertical\" == options.ScrollDirectionType then\n\t\tdir = 1\n\telseif \"Horizontal\" == options.ScrollDirectionType then\n\t\tdir = 2\n\telse\n\t\tdir = 0\n\tend\n\tnode:setDirection(dir)\n\tnode:setBounceable(options.IsBounceEnabled or false)\n\n\treturn node\nend\n\nfunction CCSUILoader:createListView(options)\n\tlocal params =\n\t\t{viewRect = cc.rect(options.Position.X, options.Position.Y, options.Size.X, options.Size.Y)}\n\n\tif 1 == options.ComboBoxIndex then\n\t\t-- single color\n\t\tparams.bgColor = cc.c4b(options.SingleColor.R or 255, options.SingleColor.G or 255, options.SingleColor.B or 255, options.BackColorAlpha or 255)\n\telseif 2 == options.ComboBoxIndex then\n\t\t-- gradient\n\t\tparams.bgStartColor = cc.c4b(options.FirstColor.R or 255, options.FirstColor.G or 255, options.FirstColor.B or 255, options.BackColorAlpha or 255)\n\t\tparams.bgEndColor = cc.c4b(options.EndColor.R or 255, options.EndColor.G or 255, options.EndColor.B or 255, options.BackColorAlpha or 255)\n\t\tparams.bgVector = cc.p(options.ColorVector.ScaleX, options.ColorVector.ScaleY)\n\tend\n\n\tlocal node = cc.ui.UIListView.new(params)\n\tlocal dir = 0\n\t-- quick 0:both 1:vertical 2:horizontal\n\tif \"Vertical\" == options.DirectionType then\n\t\tdir = 1\n\telseif \"Horizontal\" == options.DirectionType then\n\t\tdir = 2\n\telse\n\t\tdir = 0\n\tend\n\tnode:setDirection(dir)\n\tnode:setBounceable(options.IsBounceEnabled or false)\n\n\treturn node\nend\n\nfunction CCSUILoader:createPageView(options)\n\tlocal params = {}\n\tparams.column = 1\n\tparams.row = 1\n\tparams.viewRect = cc.rect(options.Position.X, options.Position.Y, options.Size.X, options.Size.Y)\n\n\tlocal node = cc.ui.UIPageView.new(params)\n\n\treturn node\nend\n\nfunction CCSUILoader:createProjectNode(options)\n\tlocal jsonFile = options.FileData.Path\n\tlocal node = CCSUILoader.new():loadFile(jsonFile)\n\n\tnode:setPosition(cc.p(options.Position.X, options.Position.Y))\n\n\treturn node\nend\n\nfunction CCSUILoader:prettyJson(json)\n\tlocal prettyNode\n\tprettyNode = function(node, parent)\n\t\tif not node then\n\t\t\treturn\n\t\tend\n\n\t\t-- 调整百分比的子节点宽高,位置\n\t\tif node.PositionPercentXEnabled then\n\t\t\tnode.Position = node.Position or {}\n\t\t\tnode.Position.X = parent.Size.X * node.PerPosition.X\n\t\t\tnode.Position.Y = parent.Size.Y * node.PerPosition.Y\n\t\tend\n\t\tif node.PercentWidthEnable then\n\t\t\tnode.Size = node.Size or {}\n\t\t\tnode.Size.X = parent.Size.X * node.PreSize.X\n\t\t\tnode.Size.Y = parent.Size.Y * node.PreSize.Y\n\t\tend\n\t\tnode.AnchorPoint = node.AnchorPoint or {} \n\t\tif not node.children then\n\t\t\treturn\n\t\tend\n\t\tif 0 == #node.children then\n\t\t\treturn\n\t\tend\n\n\t\tfor i,v in ipairs(node.children) do\n\t\t\tprettyNode(v, node)\n\t\tend\n\tend\n\n\tprettyNode(json)\nend\n\n\n\nreturn CCSUILoader\n\n");

        if (Input.GetKeyDown(KeyCode.A))
            for (var i = 0; i < 10000; i++)
                SendString("发送这串数据,此时的I是多少呢:" + i);

        #endregion
    }

    private void OnDestroy()
    {
        Clear();
    }

    private void Clear()
    {
        _tcpClient.Dispose();

        // 清空队列并释放字节数组
        while (_needHandleBytes.TryDequeue(out var dataBytes))
            // 释放字节数组
            Array.Clear(dataBytes, 0, dataBytes.Length);
    }

    public async void Connect()
    {
        _tcpClient = new TcpClient(AddressFamily.InterNetwork);

        try
        {
            // 连接服务器，设置超时时间
            await Task.WhenAny(_tcpClient.ConnectAsync(IPAddress.Parse(ip), port), Task.Delay(3000));
            if (!_tcpClient.Connected)
            {
                Clear();
                Debug.LogError("连接超时!");
                throw new TimeoutException();
            }

            _stream = _tcpClient.GetStream();
            Debug.Log("连接成功!");

            await Task.Run(HandleReceiving);
        }
        catch (TimeoutException e)
        {
            Debug.LogError(e);
            throw;
        }
        catch (SocketException e)
        {
            Debug.LogError(e);
            throw;
        }
        catch (Exception e)
        {
            Debug.LogError(e);
            throw;
        }
    }

    private async void HandleReceiving()
    {
        while (true)
        {
            _ = await _stream.ReadAsync(_headBytes, 0, headLength);

            var dataLenght = BitConverter.ToInt32(_headBytes);

            if (dataLenght == 0) continue;

            var dataBytes = new byte[dataLenght];

            var bytesReceived = await _stream.ReadAsync(dataBytes, 0, dataLenght);

            if (bytesReceived > 0)
                _needHandleBytes.Enqueue(dataBytes);
        }
    }

    private void HandleStr(byte[] dataBytes)
    {
        Debug.Log($"收到:{Encoding.UTF8.GetString(dataBytes, 0, dataBytes.Length)}");
    }


    public void SendString(string str)
    {
        // 发送数据
        var strBytes = Encoding.UTF8.GetBytes($"{str}");
        HandleSending(strBytes);
    }

    private async void HandleSending(byte[] dataBytes)
    {
        var dataLenght = dataBytes.Length;
        var headBytes = BitConverter.GetBytes(dataLenght);
        var packageLength = headLength + dataLenght;
        var packageBytes = new byte[packageLength];
        Buffer.BlockCopy(headBytes, 0, packageBytes, 0, headLength);
        Buffer.BlockCopy(dataBytes, 0, packageBytes, headLength, dataLenght);
        await _stream.WriteAsync(packageBytes, 0, packageBytes.Length);
    }
}